              <div align="right">
${TARGET="offline"}                <a href="${LDAP_SDK_HOME_URL}" style="font-size: 85%">LDAP SDK Home Page</a>
                <br>
                <a href="${BASE}index.${EXTENSION}" style="font-size: 85%">Product Information</a>
                <br>
                <a href="index.${EXTENSION}" style="font-size: 85%">Getting Started with the LDAP SDK</a>
              </div>

              <h2>Using Standard Controls</h2>

              <p>
                The UnboundID LDAP SDK for Java provides support for a number of common controls
                that are either defined in RFCs, or are based on IETF Internet Drafts that are
                considered stable enough to be implemented by many directory server
                implementations.
              </p>

              <p></p>
              <h3>Including Controls in Requests</h3>

              <p>
                All of the request objects provided by the UnboundID LDAP SDK for Java allow you to
                include controls in the request.  You can include the controls in the constructor
                when creating the request object, or you can use the <tt>getControls</tt>,
                <tt>setControls</tt>, <tt>addControl</tt>, <tt>removeControl</tt>, and
                <tt>clearControls</tt> methods to interact with the set of request controls after
                the request has been created.
              </p>

              <p>
                Note that while the LDAP SDK does not provide an explicit UnboundRequest object,
                you can include one or more controls in an unbind request by calling the
                <tt>close(Control[] controls)</tt> method in the <tt>LDAPConnection</tt> class.
              </p>

              <p></p>
              <h3>Accessing Response Controls</h3>

              <p>
                Whenever an operation completes successfully, it will return an <tt>LDAPResult</tt>
                object (or one of its subclasses, like <tt>BindResult</tt>).  The set of response
                controls provided by the server can be obtained using the
                <tt>getResponseControls</tt> method on that <tt>LDAPResult</tt> object.
              </p>

              <p>
                If an operation does not complete successfully, then the SDK may instead throw an
                <tt>LDAPException</tt> (or one of its subclasses, like
                <tt>LDAPSearchException</tt>).  In this case, the set of response controls provided
                by the server can be obtained using the <tt>getResponseControls</tt> method on that
                <tt>LDAPException</tt> object.
              </p>

              <p>
                Search operations may cause the server to send multiple response messages to the
                client (one for each entry or reference, and then a final search result done
                message to indicate that the search is complete).  When the search completes, the
                <tt>SearchResult</tt> object returned (or <tt>LDAPSearchException</tt> that is
                thrown) will have the controls included in the search result done message, and they
                can be obtained using the <tt>getResponseControls</tt> method as described above.
                However, each search result entry or search result reference may include its own
                set of controls, and the <tt>getControls</tt> method may be used to obtain that set
                of controls from the associated <tt>SearchResultEntry</tt> or
                <tt>SearchResultReference</tt> object.
              </p>

              <p>
                Note that whenever the LDAP SDK receives a response containing one or more
                controls, it will attempt to decode that control as the most specific type of
                object that it can.  For example, if a response control has an OID of
                "2.16.840.1.113730.3.4.15", then the LDAP SDK will attempt to decode it as an
                <tt>AuthorizationIdentityResponseControl</tt> rather than just as a generic
                <tt>Control</tt> object.  This will automatically happen for all types of response
                controls supported by the UnboundID LDAP SDK for Java, but if you define your own
                custom control for use with the SDK, then you may call the
                <tt>Control.registerDecodableControl</tt> method to allow the SDK to attempt to
                perform this specific decoding for that custom response control.
              </p>

              <p></p>
              <h3>The Authorization Identity Controls</h3>

              <p>
                The authorization identity request and response controls are defined in RFC 3829
                and may be used by the server to request information about the new authorization
                identity for a client connection after a bind operation is processed.
              </p>

              <p>
                The <tt>AuthorizationIdentityRequestControl</tt> may be included in a bind request
                to indicate that the server should include a corresponding
                <tt>AuthorizationIdentityResponseControl</tt> in the bind response.  If the bind is
                successful, then the bind response may include this response control and its
                <tt>getAuthorizationID</tt> method may be used to obtain the authorization ID
                returned by the server.
              </p>

              <p>
                For example, the following code may be used to authenticate to the server and try
                to retrieve the authorization identity from the response:
              </p>

              <pre>
String authzID = null;
BindRequest bindRequest =
     new SimpleBindRequest("uid=test.user,ou=People,dc=example,dc=com",
          "password", new AuthorizationIdentityRequestControl());

BindResult bindResult = connection.bind(bindRequest);
AuthorizationIdentityResponseControl authzIdentityResponse =
     AuthorizationIdentityResponseControl.get(bindResult);
if (authzIdentityResponse != null)
{
  authzID = authzIdentityResponse.getAuthorizationID();
}
</pre>

              <p></p>
              <h3>The LDAP Assertion Request Control</h3>

              <p>
                The LDAP assertion request control is defined in RFC 4528.  It provides the ability
                to define a search filter that must match the target entry in order for the
                associated operation to be processed.  If the target entry does not match the
                assertion filter, then the server should not process that operation but instead
                return a response with the "Authorization Failed" result (which in most cases will
                cause the SDK to throw an <tt>LDAPException</tt> for the associated operation).
                There is no corresponding response control.
              </p>

              <p>
                For example, the following code may be used to modify an entry to set an entry's
                "accountBalance" value to "543.21" only if the current value is "1234.56" (which
                can help prevent against race conditions that result from multiple concurrent
                changes to the value):
              </p>

              <pre>
Modification mod = new Modification(ModificationType.REPLACE,
     "accountBalance", "543.21");
ModifyRequest modifyRequest =
     new ModifyRequest("uid=john.doe,ou=People,dc=example,dc=com", mod);
modifyRequest.addControl(
     new AssertionRequestControl("(accountBalance=1234.56)"));

LDAPResult modifyResult;
try
{
  modifyResult = connection.modify(modifyRequest);
  // If we've gotten here, then the modification was successful.
}
catch (LDAPException le)
{
  modifyResult = le.toLDAPResult();
  ResultCode resultCode = le.getResultCode();
  String errorMessageFromServer = le.getDiagnosticMessage();
  if (resultCode == ResultCode.ASSERTION_FAILED)
  {
    // The modification failed because the account balance value wasn't
    // what we thought it was.
  }
  else
  {
    // The modification failed for some other reason.
  }
}
</pre>

              <p></p>
              <h3>The LDAP Read Entry Controls</h3>

              <p>
                The LDAP read entry controls are defined in RFC 4527 and make it possible to
                retrieve the contents of an entry either immediately before or immediately after
                processing an operation.  The pre-read request control may be used with delete,
                modify, and modify DN operations to retrieve the entry as it appeared immediately
                before the operation, and the post-read control may be used with the add, modify,
                and modify DN operations to retrieve the entry as it appeared immediately after the
                operation.
              </p>

              <p>
                The <tt>PreReadRequestControl</tt> and <tt>PostReadRequestControl</tt> objects are
                very similar, and may specify an optional set of attributes to be included in the
                entry that is returned.  Similarly, the corresponding
                <tt>PreReadResponseControl</tt> and <tt>PostReadResponseControl</tt> objects are
                also nearly identical, and provide access to the requested entry through the
                <tt>getEntry</tt> method.
              </p>

              <p>
                For example, the following code will increment the value of the "test-counter"
                attribute by one and will then use the post-read controls to determine what the new
                value is:
              </p>

              <pre>
// Create a modify request that we can use to increment the value of a
// custom attribute named "test-counter".
ModifyRequest modifyRequest = new ModifyRequest(
     "uid=test.user,ou=People,dc=example,dc=com",
     new Modification(ModificationType.INCREMENT,
          "test-counter", // The attribute to increment.
          "1")); // The amount by which to increment the value.

// Update the modify request to add both pre-read and post-read request
// controls to see what the entry value was before and after the change.
// We only care about getting the test-counter attribute.
modifyRequest.setControls(
     new PreReadRequestControl("test-counter"),
     new PostReadRequestControl("test-counter"));

// Process the modify operation in the server.
LDAPResult modifyResult;
try
{
  modifyResult = connection.modify(modifyRequest);
  // If we got here, then the modification should have been successful.
}
catch (LDAPException le)
{
  // This indicates that the operation did not complete successfully.
  modifyResult = le.toLDAPResult();
  ResultCode resultCode = le.getResultCode();
  String errorMessageFromServer = le.getDiagnosticMessage();
}
LDAPTestUtils.assertResultCodeEquals(modifyResult, ResultCode.SUCCESS);

// Get the pre-read and post-read response controls from the server and
// retrieve the before and after values for the test-counter attribute.
LDAPTestUtils.assertHasControl(modifyResult,
     PreReadResponseControl.PRE_READ_RESPONSE_OID);
PreReadResponseControl preReadResponse =
     PreReadResponseControl.get(modifyResult);
Integer beforeValue =
     preReadResponse.getEntry().getAttributeValueAsInteger("test-counter");

LDAPTestUtils.assertHasControl(modifyResult,
     PostReadResponseControl.POST_READ_RESPONSE_OID);
PostReadResponseControl postReadResponse =
     PostReadResponseControl.get(modifyResult);
Integer afterValue =
     postReadResponse.getEntry().getAttributeValueAsInteger("test-counter");
</pre>

              <p></p>
              <h3>The LDAP Subentries Request Control</h3>

              <p>
                The LDAP subentries request control is defined in draft-ietf-ldup-subentry, and may
                be used to indicate that a search operation should include matching entries that
                have the ldapSubentry object class (which are normally excluded from search
                results).  There is no corresponding response control.
              </p>

              <p>
                The following example illustrates the use of the LDAP subentries request control to
                retrieve a known subentry that will not be retrieved with a normal search.
              </p>

              <pre>
// First, perform a search to retrieve an entry with a cn of "test subentry"
// but without including the subentries request control.  This should not
// return any matching entries.
SearchRequest searchRequest = new SearchRequest("dc=example,dc=com",
     SearchScope.SUB, Filter.createEqualityFilter("cn", "test subentry"));
SearchResult resultWithoutControl = connection.search(searchRequest);
LDAPTestUtils.assertResultCodeEquals(resultWithoutControl,
     ResultCode.SUCCESS);
LDAPTestUtils.assertEntriesReturnedEquals(resultWithoutControl, 0);

// Update the search request to add a subentries request control so that
// subentries should be included in search results.  This should cause the
// subentry to be returned.
searchRequest.addControl(new SubentriesRequestControl());
SearchResult resultWithControl = connection.search(searchRequest);
LDAPTestUtils.assertResultCodeEquals(resultWithControl, ResultCode.SUCCESS);
LDAPTestUtils.assertEntriesReturnedEquals(resultWithControl, 1);
</pre>

              <p></p>
              <h3>The ManageDsaIT Request Control</h3>

              <p>
                The ManageDsaIT request control is defined in RFC 3296 and may be used to request
                that the directory server treat all entries as if they were regular entries.  There
                is no corresponding response control.
              </p>

              <p>
                For example, if the entry "ou=referral entry,dc=example,dc=com" is actually a
                smart referral that points to an entry on another server, then normal attempts to
                interact with that entry would cause the server to send a referral informing the
                client that it should send the request to the other server.  If you really do
                want to interact with the "ou=referral entry,dc=example,dc=com" smart referral
                entry (e.g., to delete or update the referral), then it will be necessary to
                include the ManageDsaIT request control, as follows:
              </p>

              <pre>
// Establish a connection to the directory server.  Even though it's the
// default behavior, we'll explicitly configure the connection to not follow
// referrals.
LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
connectionOptions.setFollowReferrals(false);
LDAPConnection connection = new LDAPConnection(connectionOptions,
     serverAddress, serverPort, bindDN, bindPassword);

// Try to delete an entry that will result in a referral.  Without the
// ManageDsaIT request control, we should get an exception.
DeleteRequest deleteRequest =
     new DeleteRequest("ou=referral entry,dc=example,dc=com");
LDAPResult deleteResult;
try
{
  deleteResult = connection.delete(deleteRequest);
}
catch (LDAPException le)
{
  // This exception is expected because we should get a referral, and
  // the connection is configured to not follow referrals.
  deleteResult = le.toLDAPResult();
  ResultCode resultCode = le.getResultCode();
  String errorMessageFromServer = le.getDiagnosticMessage();
  String[] referralURLs = le.getReferralURLs();
}
LDAPTestUtils.assertResultCodeEquals(deleteResult, ResultCode.REFERRAL);
LDAPTestUtils.assertHasReferral(deleteResult);

// Update the delete request to include the ManageDsaIT request control,
// which will cause the server to try to delete the referral entry instead
// of returning a referral response.  We'll assume that the delete is
// successful.
deleteRequest.addControl(new ManageDsaITRequestControl());
try
{
  deleteResult = connection.delete(deleteRequest);
}
catch (LDAPException le)
{
  // The delete shouldn't trigger a referral, but it's possible that the
  // operation failed for some other reason (e.g., entry doesn't exist, the
  // user doesn't have permission to delete it, etc.).
  deleteResult = le.toLDAPResult();
}
LDAPTestUtils.assertResultCodeEquals(deleteResult, ResultCode.SUCCESS);
LDAPTestUtils.assertMissingReferral(deleteResult);

connection.close();
</pre>

              <p></p>
              <h3>The Matched Values Control</h3>

              <p>
                The matched values request control is defined in RFC 3876 and may be used in a
                search operation to request that only attribute values matching one or more filters
                should be included in matching entries.  This can be useful, for example, if an
                attribute has a large number of values and you are only interested in values
                matching a specified set of criteria.  There is no corresponding response control.
              </p>

              <p>
                The following code demonstrates the use of the matched values control to retrieve
                only a portion of the values for an attribute:
              </p>

              <pre>
// Ensure that a test user has multiple description values.
LDAPResult modifyResult = connection.modify(
     "uid=test.user,ou=People,dc=example,dc=com",
     new Modification(ModificationType.REPLACE,
          "description", // Attribute name
          "first", "second", "third", "fourth")); // Attribute values.
assertResultCodeEquals(modifyResult, ResultCode.SUCCESS);

// Perform a search to retrieve the test user entry without using the
// matched values request control.  This should return all four description
// values.
SearchRequest searchRequest = new SearchRequest(
     "uid=test.user,ou=People,dc=example,dc=com", // Base DN
     SearchScope.BASE, // Scope
     Filter.createPresenceFilter("objectClass"), // Filter
     "description"); // Attributes to return.
SearchResultEntry entryRetrievedWithoutControl =
     connection.searchForEntry(searchRequest);
Attribute fullDescriptionAttribute =
     entryRetrievedWithoutControl.getAttribute("description");
int numFullDescriptionValues = fullDescriptionAttribute.size();

// Update the search request to include a matched values control that will
// only return values that start with the letter "f".  In our test entry,
// this should just match two values ("first" and "fourth").
searchRequest.addControl(new MatchedValuesRequestControl(
     MatchedValuesFilter.createSubstringFilter("description", // Attribute
          "f", // subInitial component
          null, // subAny components
          null))); // subFinal component
SearchResultEntry entryRetrievedWithControl =
     connection.searchForEntry(searchRequest);
Attribute partialDescriptionAttribute =
     entryRetrievedWithControl.getAttribute("description");
int numPartialDescriptionValues = partialDescriptionAttribute.size();
</pre>

              <p></p>
              <h3>The Password Expired and Password Expiring Response Controls</h3>

              <p>
                The password expired and password expiring controls are defined in
                draft-vchu-ldap-pwd-policy and may be returned by the server with a bind response
                to indicate that the user's password either is expired or is about to expire.  If
                the password is about to expire, then the password expiring response control will
                include the length of time until the password actually expires.
              </p>

              <p>
                For example:
              </p>

              <pre>
// Send a simple bind request to the directory server.
BindRequest bindRequest =
     new SimpleBindRequest("uid=test.user,ou=People,dc=example,dc=com",
          "password");
BindResult bindResult;
boolean bindSuccessful;
boolean passwordExpired;
boolean passwordAboutToExpire;
try
{
  bindResult = connection.bind(bindRequest);

  // If we got here, the bind was successful and we know the password was
  // not expired.  However, we shouldn't ignore the result because the
  // password might be about to expire.  To determine whether that is the
  // case, we should see if the bind result included a password expiring
  // control.
  bindSuccessful = true;
  passwordExpired = false;

  PasswordExpiringControl expiringControl =
       PasswordExpiringControl.get(bindResult);
  if (expiringControl != null)
  {
    passwordAboutToExpire = true;
    int secondsToExpiration = expiringControl.getSecondsUntilExpiration();
  }
  else
  {
    passwordAboutToExpire = false;
  }
}
catch (LDAPException le)
{
  // If we got here, then the bind failed.  The failure may or may not have
  // been due to an expired password.  To determine that, we should see if
  // the bind result included a password expired control.
  bindSuccessful = false;
  passwordAboutToExpire = false;
  bindResult = new BindResult(le.toLDAPResult());
  ResultCode resultCode = le.getResultCode();
  String errorMessageFromServer = le.getDiagnosticMessage();

  PasswordExpiredControl expiredControl =
       PasswordExpiredControl.get(bindResult);
  if (expiredControl != null)
  {
    passwordExpired = true;
  }
  else
  {
    passwordExpired = false;
  }
}
</pre>

              <p></p>
              <h3>The Persistent Search and Entry Change Notification Controls</h3>

              <p>
                The persistent search request control is defined in draft-ietf-ldapext-psearch and
                may be included in a search request to indicate that the server should send a
                search result entry message each time an entry matching the associated search
                criteria is updated in the server.  This type of search is somewhat unique in that
                it generally doesn't end on its own, and it can also return the same entry multiple
                times (or at least different versions of that entry) if the same entry is updated
                multiple times.
              </p>

              <p>
                Note that because persistent searches don't necessarily have a defined end and you
                will want to be able to do something with entries as soon as they are updated, then
                you should only use the persistent search request control in a search that uses a
                <tt>SearchResultListener</tt> to handle the entries that get returned.  Those
                entries may optionally contain the entry change notification control, which can
                include additional information about the update, including the type of operation
                (add, delete, modify, or modify DN), and potentially the change number and/or
                previous DN (if it was a modify DN operation).
              </p>

              <p>
                For example, the following code will begin an asynchronous search including the
                persistent search control that will notify the client of all changes to entries at
                or below "dc=example,dc=com".
              </p>

              <pre>
SearchRequest persistentSearchRequest = new SearchRequest(
     asyncSearchListener, "dc=example,dc=com", SearchScope.SUB,
     Filter.createPresenceFilter("objectClass"));
persistentSearchRequest.addControl(new PersistentSearchRequestControl(
     PersistentSearchChangeType.allChangeTypes(), // Notify change types.
     true, // Only return new changes, don't match existing entries.
     true)); // Include change notification controls in search entries.

// Launch the persistent search as an asynchronous operation.
AsyncRequestID persistentSearchRequestID =
     connection.asyncSearch(persistentSearchRequest);

// Modify an entry that matches the persistent search criteria.  This
// should cause the persistent search listener to be notified.
LDAPResult modifyResult = connection.modify(
     "uid=test.user,ou=People,dc=example,dc=com",
     new Modification(ModificationType.REPLACE, "description", "test"));

// Verify that the persistent search listener was notified....

// Since persistent search operations don't end on their own, we need to
// abandon the search when we don't need it anymore.
connection.abandon(persistentSearchRequestID);
</pre>

              <p></p>
              <h3>The Proxied Authorization Request Controls</h3>

              <p>
                The proxied authorization request controls may be used to request that an operation
                be processed under the authority of another user.  Proxied authorization is
                frequently used in conjunction with connection pools because it allows the client
                to maintain a set of connections authenticated as a given user (which has
                permission to use the proxied authorization control) and use those connections to
                perform operations in the server under the authority of the actual end user that
                triggered the request.  There is no corresponding response control.
              </p>

              <p>
                The UnboundID LDAP SDK for Java supports two different versions of the proxied
                authorization control.  A number of directory servers implement support for the
                proxied authorization V1 control, which is defined in early versions of the
                draft-weltman-ldapv3-proxy Internet Draft.  More recently, however, this
                specification has been updated and released as RFC 4370 to define the proxied
                authorization V2 control.  The two controls have different OIDs, and also take
                different arguments.  The target user for the proxied authorization V1 control must
                be a distinguished name, whereas the target user for the proxied authorization V2
                control must be an authorization ID (as defined in section 9 of RFC 2829, e.g.,
                "dn:uid=john.doe,ou=People,dc=example,dc=com" or "u:john.doe").  In general, it is
                recommended to use the proxied authorization V2 control in preference to V1 when
                possible, since it has a more well-defined specification.
              </p>

              <p>
                For example, the following code demonstrates the use of the proxied authorization
                V2 request control to delete an entry under the authority of the user with DN
                "uid=alternate.user,ou=People,dc=example,dc=com":
              </p>

              <pre>
// Create a delete request to delete an entry.  Include the proxied
// authorization v2 request control in the delete request so that the
// delete will be processed as the user with username "alternate.user"
// instead of the user that's actually authenticated on the connection.
DeleteRequest deleteRequest =
     new DeleteRequest("uid=test.user,ou=People,dc=example,dc=com");
deleteRequest.addControl(new ProxiedAuthorizationV2RequestControl(
     "u:alternate.user"));

LDAPResult deleteResult;
try
{
  deleteResult = connection.delete(deleteRequest);
  // If we got here, then the delete was successful.
}
catch (LDAPException le)
{
  // The delete failed for some reason.  In addition to all of the normal
  // reasons a delete could fail (e.g., the entry doesn't exist, or has one
  // or more subordinates), proxied-authorization specific failures may
  // include that the authenticated user doesn't have permission to use the
  // proxied authorization control to impersonate the alternate user, that
  // the alternate user doesn't exist, or that the alternate user doesn't
  // have permission to perform the requested operation.
  deleteResult = le.toLDAPResult();
  ResultCode resultCode = le.getResultCode();
  String errorMessageFromServer = le.getDiagnosticMessage();
}
</pre>

              <p></p>
              <h3>The Server-Side Sort Controls</h3>

              <p>
                The server-side sort request control may be used to request that the server sort
                the set of matching entries before returning them to the client.  A corresponding
                response control can provide information about the result of the sort processing.
              </p>

              <p>
                Note that this can be an expensive operation in some cases, so LDAP client
                developers that may wish to use this feature should first discuss it with an
                administrator of the target directory to determine whether that is appropriate and
                if the server is appropriately configured to process such requests efficiently.  As
                an alternative, it may be desirable to perform client-side sorting, which will
                significantly reduce the load on the server.
              </p>

              <p>
                The following example demonstrates the use of the server-side sort controls to
                request entries matching a search request in varying orders:
              </p>

              <pre>
// Perform a search to get all user entries sorted by last name, then by
// first name, both in ascending order.
SearchRequest searchRequest = new SearchRequest(
     "ou=People,dc=example,dc=com", SearchScope.SUB,
     Filter.createEqualityFilter("objectClass", "person"));
searchRequest.addControl(new ServerSideSortRequestControl(
     new SortKey("sn"), new SortKey("givenName")));
SearchResult lastNameAscendingResult;
try
{
  lastNameAscendingResult = connection.search(searchRequest);
  // If we got here, then the search was successful.
}
catch (LDAPSearchException lse)
{
  // The search failed for some reason.
  lastNameAscendingResult = lse.getSearchResult();
  ResultCode resultCode = lse.getResultCode();
  String errorMessageFromServer = lse.getDiagnosticMessage();
}

// Get the response control and retrieve the result code for the sort
// processing.
LDAPTestUtils.assertHasControl(lastNameAscendingResult,
     ServerSideSortResponseControl.SERVER_SIDE_SORT_RESPONSE_OID);
ServerSideSortResponseControl lastNameAscendingResponseControl =
     ServerSideSortResponseControl.get(lastNameAscendingResult);
ResultCode lastNameSortResult =
     lastNameAscendingResponseControl.getResultCode();


// Perform the same search, but this time request the results to be sorted
// in descending order by first name, then last name.
searchRequest.setControls(new ServerSideSortRequestControl(
     new SortKey("givenName", true), new SortKey("sn", true)));
SearchResult firstNameDescendingResult;
try
{
  firstNameDescendingResult = connection.search(searchRequest);
  // If we got here, then the search was successful.
}
catch (LDAPSearchException lse)
{
  // The search failed for some reason.
  firstNameDescendingResult = lse.getSearchResult();
  ResultCode resultCode = lse.getResultCode();
  String errorMessageFromServer = lse.getDiagnosticMessage();
}

// Get the response control and retrieve the result code for the sort
// processing.
LDAPTestUtils.assertHasControl(firstNameDescendingResult,
     ServerSideSortResponseControl.SERVER_SIDE_SORT_RESPONSE_OID);
ServerSideSortResponseControl firstNameDescendingResponseControl =
     ServerSideSortResponseControl.get(firstNameDescendingResult);
ResultCode firstNameSortResult =
     firstNameDescendingResponseControl.getResultCode();
</pre>

              <p></p>
              <h3>The Simple Paged Results Control</h3>

              <p>
                The simple paged results control is defined in RFC 2696 and allows the client to
                read "pages" of results (where each "page" contains a subset of the overall set of
                matching entries).  It is similar to the virtual list view control discussed below,
                although it does not provide as many options, but also does not include the
                constraint that the results be sorted.  Some servers support the simple paged
                results control but not the virtual list view control, or vice versa, so it is
                recommended to contact your directory administrator to determine what options are
                available.
              </p>

              <p>
                The simple paged results control is both a request and response control.  On the
                first request to the server, it should contain a page size but no "cookie" value.
                When the server has finished sending the specified number of entries, it will
                include the simple paged results control in the search result done message, and if
                there are more results to return then that response will include a cookie value
                that can be used to help the server figure out where to resume processing for the
                next page of results.
              </p>

              <p>
                The following code demonstrates the use of the simple paged results control to
                return all users in the server, retrieving up to 10 entries at a time:
              </p>

              <pre>
// Perform a search to retrieve all users in the server, but only retrieving
// ten at a time.
int numSearches = 0;
int totalEntriesReturned = 0;
SearchRequest searchRequest = new SearchRequest("dc=example,dc=com",
     SearchScope.SUB, Filter.createEqualityFilter("objectClass", "person"));
ASN1OctetString resumeCookie = null;
while (true)
{
  searchRequest.setControls(
       new SimplePagedResultsControl(10, resumeCookie));
  SearchResult searchResult = connection.search(searchRequest);
  numSearches++;
  totalEntriesReturned += searchResult.getEntryCount();
  for (SearchResultEntry e : searchResult.getSearchEntries())
  {
    // Do something with each entry...
  }

  LDAPTestUtils.assertHasControl(searchResult,
       SimplePagedResultsControl.PAGED_RESULTS_OID);
  SimplePagedResultsControl responseControl =
       SimplePagedResultsControl.get(searchResult);
  if (responseControl.moreResultsToReturn())
  {
    // The resume cookie can be included in the simple paged results
    // control included in the next search to get the next page of results.
    resumeCookie = responseControl.getCookie();
  }
  else
  {
    break;
  }
}
</pre>

              <p></p>
              <h3>The Subtree Delete Request Control</h3>

              <p>
                The subtree delete request control is defined in draft-armijo-ldap-treedelete and
                may be used to request that the server should delete the specified entry and all of
                its subordinates.  Without this control, if an entry has one or more subordinates,
                then the server will return a "not allowed on non-leaf" result and will refuse to
                process the delete.  There is no corresponding response control.
              </p>

              <p>
                Note that performing a subtree delete on an entry with a large number of
                subordinates can be a very expensive operation.  If you wish to use this control on
                a large subtree, then it is recommended that you first discuss it with the
                directory administrator.
              </p>

              <p>
                The following example demonstrates the use of the subtree delete control:
              </p>

              <pre>
// First, try to delete an entry that has children, but don't include the
// subtree delete control.  This delete attempt should fail, and the
// "NOT_ALLOWED_ON_NONLEAF" result is most appropriate if the failure reason
// is that the entry has subordinates.
DeleteRequest deleteRequest =
     new DeleteRequest("ou=entry with children,dc=example,dc=com");
LDAPResult resultWithoutControl;
try
{
  resultWithoutControl = connection.delete(deleteRequest);
  // We shouldn't get here because the delete should fail.
}
catch (LDAPException le)
{
  // This is expected because the entry has children.
  resultWithoutControl = le.toLDAPResult();
  ResultCode resultCode = le.getResultCode();
  String errorMessageFromServer = le.getDiagnosticMessage();
}
LDAPTestUtils.assertResultCodeEquals(resultWithoutControl,
     ResultCode.NOT_ALLOWED_ON_NONLEAF);

// Update the delete request to include the subtree delete request control
// and try again.
deleteRequest.addControl(new SubtreeDeleteRequestControl());
LDAPResult resultWithControl;
try
{
  resultWithControl = connection.delete(deleteRequest);
  // The delete should no longer be rejected just because the target entry
  // has children.
}
catch (LDAPException le)
{
  // The delete still failed for some other reason.
  resultWithControl = le.toLDAPResult();
  ResultCode resultCode = le.getResultCode();
  String errorMessageFromServer = le.getDiagnosticMessage();
}
LDAPTestUtils.assertResultCodeEquals(resultWithControl, ResultCode.SUCCESS);
</pre>

              <p></p>
              <h3>The Virtual List View Controls</h3>

              <p>
                The virtual list view controls are defined in draft-ietf-ldapext-ldapv3-vlv, and
                may be used to retrieve arbitrary pages of results matching the given search
                criteria.  It is similar to the simple paged results control, but requires that the
                search request also include the server-side sort request (which is not a
                requirement with the simple paged results control) and also allows the client to
                request arbitrary pages of the results (whereas the simple paged results control
                only allows sequential iteration through the result pages).  The page of results to
                be returned can be specified either by offset (the position of the target entry in
                the sorted result set) or based on the value of the primary sort key (e.g., start
                at the entry with a primary sort key value greater than or equal to "k").
              </p>

              <p>
                Another benefit of the virtual list view control over the simple paged results
                control is that it can provide the client with an estimate of the total number of
                entries that match the search.  This is important to help the client know when it
                has reached the end of the results.
              </p>

              <p>
                The following example illustrates the use of the virtual list view request control
                to return all users in server, retrieving up to 10 entries at a time:
              </p>

              <pre>
// Perform a search to retrieve all users in the server, but only retrieving
// ten at a time.  Ensure that the users are sorted in ascending order by
// last name, then first name.
int numSearches = 0;
int totalEntriesReturned = 0;
SearchRequest searchRequest = new SearchRequest("dc=example,dc=com",
     SearchScope.SUB, Filter.createEqualityFilter("objectClass", "person"));
int vlvOffset = 1;
int vlvContentCount = 0;
ASN1OctetString vlvContextID = null;
while (true)
{
  // Note that the VLV control always requires the server-side sort
  // control.
  searchRequest.setControls(
       new ServerSideSortRequestControl(new SortKey("sn"),
            new SortKey("givenName")),
       new VirtualListViewRequestControl(vlvOffset, 0, 9, vlvContentCount,
            vlvContextID));
  SearchResult searchResult = connection.search(searchRequest);
  numSearches++;
  totalEntriesReturned += searchResult.getEntryCount();
  for (SearchResultEntry e : searchResult.getSearchEntries())
  {
    // Do something with each entry...
  }

  LDAPTestUtils.assertHasControl(searchResult,
       VirtualListViewResponseControl.VIRTUAL_LIST_VIEW_RESPONSE_OID);
  VirtualListViewResponseControl vlvResponseControl =
       VirtualListViewResponseControl.get(searchResult);
  vlvContentCount = vlvResponseControl.getContentCount();
  vlvOffset += 10;
  vlvContextID = vlvResponseControl.getContextID();
  if (vlvOffset &gt; vlvContentCount)
  {
    break;
  }
}
</pre>
